//
// Created by 33145 on 2023/4/18.
//

#include "mysql_connection_pool.h"

namespace sylar {
  namespace sql {

    static sylar::Logger::ptr g_logger = SYLAR_LOG_NAME("system");

    ConnectionPool::ConnectionPool() {
      m_CurConn = 0;
      m_FreeConn = 0;
    }

    ConnectionPool::~ConnectionPool()
    {
      // printf("hello\n");
      DestroyPool();
      // printf("world\n");
    }

    ConnectionPool *ConnectionPool::GetInstance() {
      static ConnectionPool conn_pool;
      return &conn_pool;
    }

    MySQL::ptr ConnectionPool::GetConnection() {
      m_sem.wait();
      MutexType::Lock lock(m_mutex);
      if (m_connList.size() <= 0 || m_FreeConn <= 0) {
        return nullptr;
      }
      auto conn = m_connList.front();
      m_connList.pop_front();

      --m_FreeConn;
      ++m_CurConn;
      return conn;
    }

    bool ConnectionPool::ReleaseConnection(MySQL::ptr conn) {
      if (!conn) {
        return false;
      }
      MutexType::Lock lock(m_mutex);
      m_connList.push_front(conn);
      ++m_FreeConn;
      --m_CurConn;
      m_sem.notify();
      return true;
    }

    int ConnectionPool::GetFreeConn() {
      MutexType::Lock lock(m_mutex);
      return this->m_FreeConn;
    }

    void ConnectionPool::DestroyPool() {
      MutexType::Lock lock(m_mutex);
      if (!m_connList.empty())
      {
        for (auto & it : m_connList)
        {
          mysql_close(it->getMySQL().get());
        }
        m_CurConn = 0;
        m_FreeConn = 0;
        m_connList.clear();
      }

    }

    void ConnectionPool::init(const std::string& user, const std::string& passWd , int max_conn_cnt, const std::string& databaseName,
                              const std::string& url, const std::string& port) {
      m_url = url;
      m_Port = port;
      m_User = user;
      m_PassWord = passWd;
      m_DatabaseName = databaseName;
      m_MaxConn = max_conn_cnt;

      for (int i = 0; i < max_conn_cnt; i++) {
        m_connList.push_front(std::make_shared<MySQL>(user, passWd, databaseName, url, port));
      }
      for (const auto& it : m_connList) {
        if (!it->connect()) {
          SYLAR_LOG_ERROR(g_logger) << "connect error: " << mysql_error(reinterpret_cast<MYSQL *>(it.get()));
          exit(1);
        } else {
          ++m_FreeConn;
        }
      }
    }


    connectionRAII::connectionRAII(MySQL::ptr* con, ConnectionPool* connPool) {
      *con = connPool->GetConnection();
      conRAII = *con;
      poolRAII = connPool;
    }

    connectionRAII::~connectionRAII() {
      poolRAII->ReleaseConnection(conRAII);
    }
  }
}